package com.gitee.srurl.snow.backwards;

import cn.hutool.core.date.SystemClock;
import com.gitee.srurl.register.DataReportHandle;
import com.gitee.srurl.snow.gen.AbstractSnowflake;
import com.gitee.srurl.snow.gen.DefaultSnowflake;
import com.gitee.srurl.snow.domain.SnowflakeData;
import lombok.extern.slf4j.Slf4j;

/**
 * 使用新机器ID 如果使用该策略处理时间回拨 则机器ID只能为0-15 16-31不允许使用
 * 时间回拨的话 会在当前机器ID的基础上+16创建一个新的 <strong style='color: red;'>临时</strong> 节点去创建ID 待回拨时间后 当前时间大于等于上次创建ID 的时间的时候
 * 销毁该对象 继续使用之前的对象
 *
 * @author zxw
 * @date 2023/4/22 10:41
 */
@Slf4j
public class UseNewWork extends AbstractBackwards {

    private final AbstractSnowflake snowflake;

    /**
     * 这个时间是主算法的最后一条数据的生成时间
     * snowflake.getLastTimestamp() 的时间是从当前时间戳开始 原理是采用当前work id 去生成id 补足主算法回拨的那段时间差
     * 也就是说如果调用getLastTimestamp() 应该是要返回主算法的最新一条时间 否则{@link DefaultSnowflake #doGetNextId()}
     * 那边判断时钟是否正常会出问题
     */
    private final long masterLastTimestamp;

    public UseNewWork(long lastTimestamp, SnowflakeData snowflakeData) {

        super(lastTimestamp, snowflakeData);
        this.masterLastTimestamp = lastTimestamp;
        // 查询该工作ID+15的工作ID历史lastTimestamp
        long historyTimestamp = -1;
        // 当前时间戳要比历史的Id生成时间戳来得大或者等于
        // （因为构造默认雪花算法的时候 会把当前时间戳+1 所以 nowTimestamp - 1 必须要大于 historyTimestamp）才行
        // 因为这个模式不允许再次时钟回拨 所以nowTimestamp <= historyTimestamp 不能为false 否则报错
        long nowTimestamp = SystemClock.now() - 1;
        if (snowflakeData == null || nowTimestamp < historyTimestamp) {
            log.error("snowflakeData为空或者historyTimestamp大于当前时间戳，historyTimestamp：{}，当前timestamp：{}，snowflakeData：{}，",
                    historyTimestamp, nowTimestamp, snowflakeData);
            throw new IllegalStateException("参数为空或者当前时间戳小于历史节点时间戳！");
        }

        if (snowflakeData.getWorkerId() > 15) {
            throw new IllegalArgumentException("使用新工作ID情况下，工作ID不允许大于15");
        }
        snowflakeData = SnowflakeData.builder()
                .epoch(snowflakeData.getEpoch())
                .workerId(snowflakeData.getWorkerId() + 15)
                .dataCenterId(snowflakeData.getDataCenterId())
                // 新的不允许再次回拨
                .timeOffset(0L)
                // 设置最新的时间戳为当前时间啊
                .lastTimestamp(nowTimestamp)
                .randomSequenceLimit(snowflakeData.getRandomSequenceLimit())
                .backwardsHandle(null)
                .build();

        snowflake = new DefaultSnowflake(nowTimestamp, snowflakeData);
    }

    @Override
    public long nextId() {
        return snowflake.nextId();
    }

    @Override
    public long getLastTimestamp() {

        return this.masterLastTimestamp;
    }

    @Override
    public void doClean() {
        // 取消上报的注册
        DataReportHandle.unRegisterReport(snowflake);
    }
}
